package Service;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import Engine.MetaScheduleProtocol;
import Engine.engine;
import Exception.ServiceException;
import PSQueue.AbstractPublication;
import PSQueue.AbstractQueue;
import PSQueue.AbstractSubscriber;
import Schema.BehaviorInterfaceDefinition;
import Schema.ProcessDefinition;
import Schema.ServiceDefinition;
import Schema.VariableDefinition;
import Schema.VariableType;

/**
 * @author cyye
 *
 */

public abstract class AbstractService implements AbstractSubscriber {
	public static final int LOCALTASK = 0;
    public static final int JAVACLASS = 1;
    public static final int WEBSERVICE = 2;
    public static final int PROCESS = 3;       
    
    //service type
    private int serviceType;
	public void setServiceType(int serviceType) {
		this.serviceType = serviceType;
	}

	public int getServiceType() {
		return serviceType;
	}
		

	//service parent
	private AbstractService m_parent;
	public void setParent(AbstractService m_parent) {
		this.m_parent = m_parent;
	}

	public AbstractService getParent() {
		return m_parent;
	}   


	//service Name
	private String m_ServiceName;
	public void setServiceName(String m_ServiceName) {
		this.m_ServiceName = m_ServiceName;
	}

	public String getServiceName() {
		return m_ServiceName;
	}		

	
	//package name
	private String packageName;
	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	public String getPackageName() {
		return packageName;
	}
	
	
	//service ID (unique)
	private String m_InstanceID = getNextInstanceID();	
	public String getInstanceID() {
		return m_InstanceID;
	}
		
	private static long nextID = 1;	
	private synchronized String getNextInstanceID() {
		// TODO Auto-generated method stub
		String id="";
		try{
		   InetAddress thisIp =InetAddress.getLocalHost();
		   id = thisIp.getHostAddress();
		} catch(Exception e) {		   	
		}
		
		id = id + ":" + nextID + ":" + System.currentTimeMillis();
		nextID++;
		return id;
	}
	
	
	//publish publication
	protected void publish(AbstractPublication pub) {
		AbstractQueue queue = engine.getEngine().getQueue();
		assert queue!=null;
		queue.publish(pub);
	}
	
	//throw exception
	protected void throwException(ServiceException thrownException) {
		assert thrownException!=null;
		String instanceID = getInstanceID();
		assert instanceID!=null;
		
		AbstractPublication pub = MetaScheduleProtocol.getServiceExceptionProtocol(instanceID, thrownException);
		publish(pub);
	}
	
	//publish completion info
	protected void publishCompletionInfo() {
		AbstractParameter out_parameter = constructOutputParameter();
		String instanceID = getInstanceID();
		assert out_parameter!=null;
		assert instanceID !=null;
		
		AbstractPublication pub = MetaScheduleProtocol.getServiceCompletionProtocol(instanceID, out_parameter);
		publish(pub);
	}
	

	//execution of service
	public abstract void execute(AbstractParameter invocationParameter);		

	//handling exception
	public void OnException(String invokedServiceName, ServiceException thrownException) {
		// TODO Auto-generated method stub		
	}
	
	
	public void OnInvocationResult(String invokedServiceName, AbstractParameter returned_parameter) {		
	}
	


	
/*
 * ***************************************************************************************
 * 
 *                             Initialize input/output parameter
 *                             
 * ***************************************************************************************                            
 */		

	//save input parameters
	private HashMap<String, Variable> m_inputParameter = new HashMap<String, Variable>();
	protected void initializeInputParameter(AbstractParameter invocationParameter) throws ServiceException {
		assert invocationParameter!=null;		        
		
		BehaviorInterfaceDefinition bi = m_serviceDefinition.getBehaviorInterface();
        assert bi!=null;
		
        List<VariableDefinition> inputVariables = bi.getInputParameter();
		assert inputVariables!=null;
		
		Iterator<VariableDefinition> it = inputVariables.iterator();
		while(it.hasNext()) {
			VariableDefinition vDef = it.next();
			String vName = vDef.getName();
			assert vName!=null;
			
			Variable variable = invocationParameter.getParamter(vName);
			if(variable ==null) throw new ServiceException("Missing input paramter!");						
			if(!compatible(variable, vDef)) throw new ServiceException("Incompatible input parameter!");
			
			m_inputParameter.put(vName, variable);
		}
	}
	
	protected synchronized Variable getInputParameterVariable(String name) {
    	assert name!=null;
    	return m_inputParameter.get(name);
    }
	
   
    //save output parameters
	protected HashMap<String, Variable> m_outputParameter = new HashMap<String, Variable>();
	protected void initializeOutputParameter() throws ServiceException {			       
		
		BehaviorInterfaceDefinition bi = m_serviceDefinition.getBehaviorInterface();
        assert bi!=null;
		
        List<VariableDefinition> outputVariables = bi.getOutputParameter();
		assert outputVariables!=null;
		
		Iterator<VariableDefinition> it = outputVariables.iterator();
		while(it.hasNext()) {
			VariableDefinition vDef = it.next();
			String vName = vDef.getName();
			assert vName!=null;
			
			Variable variable = initializeVariable(vDef);			
			if(variable ==null) throw new ServiceException("Invalid output variable definition!");									
			
			m_outputParameter.put(vName, variable);
		}
	}
	
	protected synchronized void updateOutputVariable(Variable v) throws ServiceException {
		assert v!=null;
		
		String name = v.getName();
		assert name!=null;
		
		Variable saved_variable = m_outputParameter.get(name);
		if(saved_variable==null) throw new ServiceException("No such variable!");
		
		if(saved_variable.getType()!= v.getType()) throw new ServiceException("Incompatible variable!");
		
		Object value = v.getValue();
		if(value==null) throw new ServiceException("Invalid variable value!");
		
		saved_variable.setValue(value);		
	}
	
	
	private boolean compatible(Variable variable, VariableDefinition vDef) {
		assert variable!=null;
		assert vDef!=null;
		
		String vname1 = variable.getName();
		String vname2 = vDef.getName();
		assert vname1!=null;
		assert vname2!=null;
		if(!vname1.equals(vname2)) return false;
		
		int vType1 = variable.getType();
		VariableType vType2 = vDef.getType();
		assert vType2!=null;
		switch(vType1) {
		case Variable.BOOLEAN: return vType2.equals(VariableType.BOOLEAN);
		case Variable.DOUBLE:  return vType2.equals(VariableType.DOUBLE);
		case Variable.FLOAT:   return vType2.equals(VariableType.FLOAT);
		case Variable.INTEGER: return vType2.equals(VariableType.INTEGER);
		case Variable.STRING:  return vType2.equals(VariableType.INTEGER);
		case Variable.OTHERS:  return vType2.equals(VariableType.USERDEFINE);
		}
		
		return false;
	}

	protected Variable initializeVariable(VariableDefinition vDef) {
        assert vDef != null;
		Variable v = new Variable();
        
		String name = vDef.getName();
        assert name!=null;
        v.setName(name);
        
        VariableType vType = vDef.getType();
        assert vType!=null;
        if(vType.equals(VariableType.BOOLEAN)) {
        	v.setType(Variable.BOOLEAN);
        } else
        	
        if(vType.equals(VariableType.DOUBLE)) {
        	v.setType(Variable.DOUBLE);
        } else
		
        if(vType.equals(VariableType.FLOAT)) {
            v.setType(Variable.FLOAT);	
        } else	
		
        if(vType.equals(VariableType.INTEGER)) {
        	v.setType(Variable.INTEGER);	
        } else	
        
        if(vType.equals(VariableType.STRING)) {
        	v.setType(Variable.STRING);
        } else
        
        if(vType.equals(VariableType.USERDEFINE)) {
           	v.setType(Variable.OTHERS);
        } else	            
        	v = null; //invalid VariableDefinition
        
        return v;
	}
	
	protected AbstractParameter constructOutputParameter() {
		// TODO Auto-generated method stub
		return null;
	}
	
	//save service definition
	protected ServiceDefinition m_serviceDefinition = null;
    public void initialize(ServiceDefinition serviceDef) {
    	m_serviceDefinition = serviceDef;
	}
}
